use std::collections::BTreeMap;

use super::*;

pub trait FromPlist: Sized {
	fn from_plist(r: &mut Parser<'_, '_, '_>) -> Result<Self, Error>;
}

pub struct Parser<'a, 'b, 'c>(pub &'a mut core::iter::Peekable<plist::Parser<'b, 'c>>);
impl<'a, 'b, 'c> Parser<'a, 'b, 'c> {
	pub fn next(&mut self) -> Result<plist::Event<'a>, Error> {
		self.0.next().ok_or(Error::Eof)
	}
	pub fn bool(&mut self) -> Result<bool, Error> {
		self.next()?.as_bool().ok_or(Error::Msg("expect bool"))
	}
	pub fn uinteger(&mut self) -> Result<u64, Error> {
		self.next()?.as_unsigned().ok_or(Error::Msg("expect unsigned"))
	}
	pub fn integer(&mut self) -> Result<i64, Error> {
		self.next()?.as_signed().ok_or(Error::Msg("expect signed"))
	}
	pub fn string(&mut self) -> Result<String, Error> {
		self.next()?.as_string().ok_or(Error::Msg("expect string"))
	}
	pub fn float(&mut self) -> Result<f64, Error> {
		self.next()?.as_float().ok_or(Error::Msg("expect float"))
	}
	pub fn array<T: FromPlist>(&mut self) -> Result<Vec<T>, Error> {
		Vec::from_plist(self)
	}
	pub fn read<T: FromPlist>(&mut self) -> Result<T, Error> {
		T::from_plist(self)
	}
	pub fn uinteger_or_float(&mut self) -> Result<UIntegerOrFloat, Error> {
		UIntegerOrFloat::from_plist(self)
	}
	pub fn integer_or_float(&mut self) -> Result<IntegerOrFloat, Error> {
		IntegerOrFloat::from_plist(self)
	}
	pub fn skip_value(&mut self) -> Result<(), Error> {
		match self.next()? {
			plist::Event::End => Err(Error::Msg("expect value")),
			plist::Event::Error(e, _) => Err(Error::Plist(e)),
			plist::Event::Array => {
				loop {
					if self.0.peek() == Some(&plist::Event::End) {
						_ = self.next();
						break;
					}
					self.skip_value()?;
				}
				Ok(())
			}
			plist::Event::Dictionary => {
				loop {
					match self.0.peek().ok_or(Error::Eof)? {
						plist::Event::End => {
							_ = self.next();
							break;
						}
						plist::Event::Key(_) => {
							_ = self.next();
							self.skip_value()?;
						}
						_ => return Err(Error::Msg("expect key")),
					}
				}
				Ok(())
			}
			_ => Ok(()),
		}
	}
	pub fn dict_start(&mut self) -> Result<(), Error> {
		matches!(self.next()?, plist::Event::Dictionary)
			.then_some(())
			.ok_or(Error::Msg("expect dict"))
	}
}

#[derive(Debug, Clone, PartialEq)]
pub enum Error {
	Eof,
	Io(String),
	Fea(String),
	Plist(plist::Error),
	Msg(&'static str),
	Integer(core::num::ParseIntError),
	Real(core::num::ParseFloatError),
}
impl core::error::Error for Error {}
impl core::fmt::Display for Error {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		match self {
			Error::Plist(s) => s.fmt(f),
			Error::Eof => f.write_str("Eof"),
			Error::Msg(msg) => f.write_str(msg),
			Error::Io(s) => f.write_str(s),
			Error::Fea(s) => f.write_str(s),
			Error::Integer(e) => e.fmt(f),
			Error::Real(e) => e.fmt(f),
		}
	}
}
impl From<std::io::Error> for Error {
	fn from(value: std::io::Error) -> Self {
		Error::Io(format!("{:?}", value))
	}
}

pub type Integer = i64;
pub type UInteger = u64;

impl FromPlist for u64 {
	fn from_plist(r: &mut Parser<'_, '_, '_>) -> Result<Self, Error> {
		r.next()?.as_unsigned().ok_or(Error::Msg("expect u64"))
	}
}
impl FromPlist for i64 {
	fn from_plist(r: &mut Parser<'_, '_, '_>) -> Result<Self, Error> {
		r.next()?.as_signed().ok_or(Error::Msg("expect i64"))
	}
}

impl FromPlist for String {
	fn from_plist(r: &mut Parser<'_, '_, '_>) -> Result<Self, Error> {
		r.string()
	}
}

#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct UIntegerOrFloat(pub f64);
impl FromPlist for UIntegerOrFloat {
	fn from_plist(r: &mut Parser<'_, '_, '_>) -> Result<Self, Error> {
		let e = r.next()?;
		Ok(UIntegerOrFloat(match e {
			plist::Event::Integer(v) if v > 0 => e.as_signed().map(|v| v as f64).ok_or(Error::Msg("as_signed fail"))?,
			plist::Event::Real(v) if v > 0. => v,
			_ => return Err(Error::Msg("expect uinteger_or_float")),
		}))
	}
}
impl core::str::FromStr for UIntegerOrFloat {
	type Err = Error;
	fn from_str(s: &str) -> Result<Self, Self::Err> {
		if let Ok(v) = s.parse::<u64>() {
			Ok(Self(v as f64))
		} else if let Ok(v) = s.parse::<f64>() {
			Ok(Self(v))
		} else {
			Err(Error::Msg("expect number"))
		}
	}
}

#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct IntegerOrFloat(pub f64);
impl FromPlist for IntegerOrFloat {
	fn from_plist(r: &mut Parser<'_, '_, '_>) -> Result<Self, Error> {
		let e = r.next()?;
		Ok(IntegerOrFloat(match e {
			plist::Event::Integer(_) => e.as_signed().map(|v| v as f64).ok_or(Error::Msg("as_signed fail"))?,
			plist::Event::Real(v) => v,
			_ => return Err(Error::Msg("expect integer_or_float")),
		}))
	}
}
impl core::str::FromStr for IntegerOrFloat {
	type Err = Error;
	fn from_str(s: &str) -> Result<Self, Self::Err> {
		if let Ok(v) = s.parse::<i64>() {
			Ok(Self(v as f64))
		} else if let Ok(v) = s.parse::<f64>() {
			Ok(Self(v))
		} else {
			Err(Error::Msg("expect number"))
		}
	}
}

impl<T: FromPlist> FromPlist for Vec<T> {
	fn from_plist(r: &mut Parser<'_, '_, '_>) -> Result<Self, Error> {
		matches!(r.next()?, plist::Event::Array)
			.then_some(())
			.ok_or(Error::Msg("expect array"))?;
		let mut this = Self::default();
		loop {
			if r.0.peek() == Some(&plist::Event::End) {
				_ = r.next();
				break;
			}
			this.push(T::from_plist(r)?);
		}
		Ok(this)
	}
}

impl<T: FromPlist> FromPlist for std::collections::BTreeMap<String, T> {
	fn from_plist(r: &mut Parser<'_, '_, '_>) -> Result<Self, Error> {
		matches!(r.next()?, plist::Event::Dictionary)
			.then_some(())
			.ok_or(Error::Msg("expect dict"))?;
		let mut this = Self::default();
		loop {
			match r.next()? {
				plist::Event::End => {
					break;
				}
				plist::Event::Key(s) => {
					let value = T::from_plist(r)?;
					this.insert(s.to_string(), value);
				}
				_ => return Err(Error::Msg("expect key")),
			}
		}
		Ok(this)
	}
}

#[derive(Debug, Clone, Default)]
pub enum Value {
	#[default]
	Empty,
	Dict(BTreeMap<String, Value>),
	Array(Vec<Value>),
	Boolean(bool),
	Data(String),
	Date(String),
	Integer(i128),
	Real(f64),
	String(String),
	Uid(u64),
}
impl FromPlist for Value {
	fn from_plist(r: &mut Parser<'_, '_, '_>) -> Result<Self, Error> {
		match r.0.peek().ok_or(Error::Eof)? {
			plist::Event::Array => return Ok(Self::Array(r.read()?)),
			plist::Event::Dictionary => return Ok(Self::Dict(r.read()?)),
			_ => Ok(match r.next()? {
				plist::Event::Error(error, _) => return Err(Error::Plist(error)),
				plist::Event::End => return Err(Error::Msg("expect value found end")),
				plist::Event::Key(_) => return Err(Error::Msg("expect value found key")),
				plist::Event::Boolean(v) => Self::Boolean(v),
				plist::Event::Data(v) => Self::Data(v.to_string()),
				plist::Event::Date(v) => Self::Date(v.to_string()),
				plist::Event::Integer(v) => Self::Integer(v),
				plist::Event::Real(v) => Self::Real(v),
				plist::Event::String(v) => Self::String(v.to_string()),
				plist::Event::Uid(v) => Self::Uid(v),
				_ => return Err(Error::Msg("expect value")),
			}),
		}
		// Ok(match evt {
		// 	plist::Event::Error(error, _, _) => return Err(Error::Plist(error)),
		// 	plist::Event::Array => Self::Array(r.read()?),
		// 	plist::Event::Dictionary => Self::Dict(r.read()?),
		// 	plist::Event::End => return Err(Error::Msg("expect value found end")),
		// 	plist::Event::Key(_) => return Err(Error::Msg("expect value found key")),
		// 	plist::Event::Boolean(v) => Self::Boolean(v),
		// 	plist::Event::Data(v) => Self::Data(v.to_string()),
		// 	plist::Event::Date(v) => Self::Date(v.to_string()),
		// 	plist::Event::Integer(v) => Self::Integer(v),
		// 	plist::Event::Real(v) => Self::Real(v),
		// 	plist::Event::String(v) => Self::String(v.to_string()),
		// 	plist::Event::Uid(v) => Self::Uid(v),
		// })
	}
}
